home *** CD-ROM | disk | FTP | other *** search
/ Aminet 3 / Aminet 3 - July 1994.iso / Aminet / misc / emu / PCDisk91b.lha / src / pcdisk.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-07-13  |  33.0 KB  |  1,065 lines

  1. /*
  2.  *        PCDISK.C
  3.  *
  4.  *        By Eddy Carroll
  5.  *
  6.  *        Based somewhat on RDF: by Olaf Seibert, which itself was based
  7.  *        on Matt Dillon's excellent PIPE: device.
  8.  *
  9.  *        This handler lets you access a hard disk partition as if the
  10.  *        entire contents of the file formed a single file which can
  11.  *        be read, written to, and seeked on. This is designed mainly
  12.  *        for use with the Commodore Bridgeboard which uses an AmigaDOS
  13.  *        file to emulate a PC hard disk. By faking an AmigaDOS file
  14.  *        like this, we can avoid the inefficiency of having to go through
  15.  *        the standard file system.
  16.  *
  17.  *        The bridgeboard actually supports two types of Amiga-mounted PC
  18.  *        partitions. Autoboot partitions are unique, and have a special
  19.  *        512 byte header at the start giving the disk geometry. The
  20.  *        bridgeboard can boot from this. Other partitions (non-autoboot)
  21.  *        don't have the header at the start. To allow the two types to
  22.  *        be interchanged, we fake the autoboot header in the handler
  23.  *        when it's required, and never actually store it on disk at all.
  24.  *
  25.  *        The bridgeboard assumes that PC disks are laid out according to
  26.  *        "standard" PC disk conventions, i.e. 17 sectors/track and 4 heads.
  27.  *        This is not required for the autoboot volume however, and it is
  28.  *        possible to use an Amiga-style geometry. This makes it possible
  29.  *        to share a bridgeboard partition with an AmigaDOS handler such
  30.  *        as MSDOSFileSystem which is supplied with CrossDOS, allowing
  31.  *        easy copying of files from the Amiga side.
  32.  * 
  33.  *        This handler uses the filename given when opening a file to
  34.  *        specify various items. Filenames can have any combination of
  35.  *        the following switches, though not all make sense when combined:
  36.  *
  37.  *        /AUTO    -- Include a 512 byte autoboot header at start of file
  38.  *        /AMIGA    -- Use the actual Amiga sector and head info
  39.  *        /IBM    -- Use the IBM settings of 4 heads, 17 sectors (default)
  40.  *        /NEW    -- Only openi new files (reject open for read access)
  41.  *        /Sx        -- Force x sectors per track
  42.  *        /Hx        -- Force x heads per cylinder
  43.  *
  44.  *        For example, accessing the file PCD: on its own gives a non-autoboot
  45.  *        partition and accessing PCD:AUTO/AMIGA gives an autoboot partition
  46.  *        which has the same layout on the PC as it does on the Amiga.
  47.  *
  48.  *        In general, you need only use PCD:/AUTO for autoboot partitions and
  49.  *        PCD: for non-autoboot partitions. Any additional filename text is
  50.  *        simply ignored, unless proceeded by a '/', in which case an error
  51.  *        is returned.
  52.  *
  53.  *        The /NEW option is intended for use with the bridgeboard JLINK
  54.  *        command to initialise a new partition for use by the Bridgeboard
  55.  *        software; the JLINK command tries to open an existing file
  56.  *        first, and if it finds one, refuses to write the new partition
  57.  *        info we need.
  58.  *
  59.  *        The following functions are handled:
  60.  *
  61.  *            Open() Close() OpenFileFromLock()
  62.  *            Read() Write()
  63.  *            Seek()
  64.  *            Examine()
  65.  *            Lock()
  66.  *            DupLock()
  67.  *            UnLock()
  68.  *
  69.  *        These are more or less just dummies which appear to do something
  70.  *        useful.
  71.  *
  72.  *        DeleteFile()
  73.  *        IsFileSystem()
  74.  *        SetComment()
  75.  *        SetDate()
  76.  *
  77.  *        Implementation note: Since we have to be re-entrant, we don't use
  78.  *        any globals at all (except for library bases, which can be safely
  79.  *        shared). This makes some things a little clumsy, but there you go.
  80.  */
  81.  
  82. #define DEBUG    0                            /* If true, full debugging msgs    */
  83. #define SNOOP    0                            /* If true, monitor read/writes    */
  84.  
  85. #ifndef LATTICE_50
  86. #include "system.h"
  87. #endif
  88.  
  89. #if DEBUG
  90. #define DB(x)    x
  91. #else
  92. #define DB(x)
  93. #endif
  94. #define P        KPrintF
  95.  
  96. #define BUFSIZE            16384                /* Buffer size for masked xfers    */
  97. #define BOOTSIZE        sizeof(BootBlock)    /* Size of special Janus Boot    */
  98.  
  99. #define DEF_COMMENT    \
  100.     "Bridgeboard compatible DOS-style file. Copyright (c) Eddy Carroll 1992."
  101.  
  102. #define IBM_HEADS        4            /* Default heads/cyl for an IBM disk    */
  103. #define IBM_SECTORS        17            /* Default secs/track for an IBM disk    */    
  104.  
  105.                                             /* Various ACTIONs supported    */
  106.                                             /* -------------------------    */
  107. #define ACTION_FIND_UPDATE            1004    /* Three variations on Open()    */
  108. #define ACTION_FIND_INPUT            1005
  109. #define ACTION_FIND_OUTPUT            1006
  110. #define ACTION_END                    1007    /* Close()                        */
  111. #define ACTION_SEEK                 1008    /* Seek()                        */
  112. #define ACTION_FH_FROM_LOCK            1026    /* 2.0, opens file from lock    */
  113. #define ACTION_IS_FILESYSTEM        1027    /* Is a file system                */
  114.  
  115. #define ST_FILE                        -3        /* Indicates a normal file        */
  116.  
  117.  
  118. #undef    BADDR
  119. #define BADDR(x)    ((void *)((ULONG)(x) << 2))    /* Convert BCPL->CPTR        */
  120. #define CTOB(x)        (((ULONG)(x)) >> 2)            /* Convert CPTR->BADDR        */
  121.  
  122. #define FNAME_LEN    50                        /* Maximum length of filename    */
  123.  
  124. #define branchto    goto                    /* (-: Avoid goto's :-)         */
  125.  
  126. typedef struct DosPacket    DosPacket;
  127. typedef struct Process        Proc;
  128. typedef struct DeviceNode    DevNode;
  129. typedef struct DosEnvec        DosEnvec;
  130. typedef struct MsgPort        MsgPort;
  131. typedef struct IOStdReq     IOStdReq;
  132. typedef unsigned char        uchar;
  133.  
  134. /*
  135.  *        This is the autoboot block that appears at the start of autoboot
  136.  *        partitions used by the bridgeboard (the PC isn't directly
  137.  *        aware of this).
  138.  */
  139. typedef struct BootBlock {
  140.     uchar        title[8];            /* Title, normally 'ABOOT'            */
  141.     UWORD        heads;                /* Heads per cylinder                */
  142.     UWORD        sectors;            /* Sectors per track                */
  143.     UWORD        cylinders;            /* Cylinders per disk                */
  144.     UWORD        filler[249];        /* Pad to 512 bytes in size            */
  145. } BootBlock;
  146.  
  147.  
  148. /*
  149.  *        This structure is used to hold details of a particular IBM
  150.  *        partition on our virtual device (block offsets are relative to
  151.  *        the start of the physical device, not our virtual device).
  152.  */
  153. typedef struct IBMInfo {
  154.     ULONG    sectors;                /* Number of sectors per track        */
  155.     ULONG    heads;                    /* Number of heads per cylinder        */
  156.     ULONG    numcyls;                /* Number of cylinders                */
  157.     ULONG    startblock;                /* Starting block number            */
  158.     ULONG    numblocks;                /* Number of blocks                    */
  159.     ULONG    isautoboot;                /* True if autoboot disk            */
  160.     ULONG    newfileonly;            /* True if only allow new files        */
  161. } IBMInfo;
  162.  
  163. /*
  164.  *        Our file structure, used to keep track of where we are etc. We
  165.  *        have an optional attachment that can be added to the end of
  166.  *        the file header, to contain a preface to the main disk file.
  167.  *        This can be used to fake an autoboot block if the PC disk is
  168.  *        an autoboot partition, and also to create "fake" files like
  169.  *        automatically generated mount-lists etc, if required. If
  170.  *        datasize is zero, no such attachment exists.
  171.  *
  172.  *        Note that the data[1] is merely a place holder; in actual fact,
  173.  *        we allocate additional space at the end of the structure to hold
  174.  *        the full amount of data needed. To avoid confusion, we let 4
  175.  *        bytes go to waste at the end (i.e. we don't compensate for the
  176.  *        fact that we've already allocated 1 longword of the array).
  177.  */
  178. typedef struct MyFile {
  179.     ULONG      start;                /* Starting byte on physical device    */
  180.     ULONG      offset;                /* Current file offset form _Start    */
  181.     ULONG      maxoffset;            /* Maximum value file_Offset can be    */
  182.     ULONG      datasize;                /* Size of the attached data info    */
  183.     ULONG      data[1];                /* Placeholder for attached data    */
  184. } MyFile;
  185.  
  186. /*
  187.  *        A standard lock structure, with the addition that we remember the
  188.  *        name used to open it, so that we can parse the options when
  189.  *        Open() is called.
  190.  */
  191. typedef struct MyLock {
  192.     struct FileLock    fl;                /* A standard AmigaDOS lock            */
  193.     IBMInfo            ibm;            /* Details about disk layout        */
  194. } MyLock;
  195.  
  196. /*
  197.  *        Transient info that needs to be passed around various functions
  198.  *        It's collected in a single structure to reduce overhead in
  199.  *        function calls.
  200.  *
  201.  *        This is mainly used to handle the 'Mask' parameter from the
  202.  *        mountlist. Any attempts to read directly from disk into memory
  203.  *        that is not wholly within the range indicated by the mask has
  204.  *        to pass through a buffer in CHIP ram, since the device can't
  205.  *        DMA directly into the indicated memory. To avoid allocating
  206.  *        CHIP ram for this buffer needlessly on systems that have no
  207.  *        such problems, we only allocate the buffer the first time it's
  208.  *        actually needed.
  209.  */
  210. typedef struct MaskData {
  211.     long    xfermask;                /* Mask to check mem buffer against */
  212.     uchar    *xferbuffer;            /* CHIP mem used for transfers        */
  213.     long    xfersize;                /* Size of above buffer                */
  214. } MaskData;
  215.  
  216.  
  217. /*
  218.  *        Prototypes and external declarations
  219.  */
  220.  
  221. extern struct ExecBase    *AbsExecBase;    /* Absolute global address 4        */
  222. struct ExecBase            *SysBase;        /* Required to make Exec calls        */
  223. struct DOSBase            *DOSBase;        /* Used for AmigaDOS calls            */
  224. extern DosPacket        *taskwait();    /* Wait for a message                */
  225.  
  226. ULONG PerformIO(MaskData *maskdata, IOStdReq *ioreq, ULONG cmd, void *buffer,
  227.                 ULONG offset, ULONG size);
  228. MyFile *OpenMyFile(IBMInfo *ibm, ULONG blocksize);
  229. int ParseFilename(uchar *filename, IBMInfo *ibm, DosEnvec *env);
  230. #if DEBUG
  231. uchar *actname();
  232. #endif
  233.  
  234.  
  235. /*
  236.  *        PCdisk_handler()
  237.  *
  238.  *        Okay, here we go. This is the main entry point. We get our
  239.  *        startup message from AmigaDOS giving us info about the device
  240.  *        characteristics etc. and then go into a loop where we process
  241.  *        packet requests until eventually we are told to die.
  242.  */
  243. void PCdisk_handler()
  244. {
  245.     MaskData    maskdata[1];    /* Pointer to memory mask info for disk i/o    */
  246.     Proc        *myproc;        /* Pointer to our process                    */
  247.     DevNode     *mynode;        /* Our device node passed in parmpkt Arg3    */
  248.     MsgPort        *ioport = NULL;    /* Port used for interaction with device    */
  249.     IOStdReq    *ioreq  = NULL;    /* We use a single i/o request for all i/o    */
  250.     DosEnvec    *env = NULL;    /* Our Dos device environment table entry    */
  251.     uchar        *default_device;/* Name of the disk device we are using        */
  252.     ULONG        default_unit;    /* What unit on this device                    */
  253.     ULONG        default_flags;    /* Any necessary flags                        */
  254.     ULONG        opencount = 0;    /* How many files & locks are open             */
  255.     ULONG        blocksize;        /* Size of a block in bytes                    */
  256.     ULONG        cylsize;        /* Size of a device cylinder in bytes        */
  257.     ULONG        startblock;        /* Start block on our partition                */
  258.     ULONG        numblocks;        /* Number of blocks on our partition        */
  259.     ULONG        numcyls;        /* Number of cylinders on our partition        */
  260.     ULONG        block_offset_mask;    /* Isolate part an offset within a blk    */
  261.     ULONG        block_align_mask;    /* Force an offset to be block aligned    */
  262.     uchar        *oneblock = NULL;    /* Pointer to mem area to hold one blk    */
  263.     uchar        devname[50];    /* Name of our DOS device (PCD: etc.)        */
  264.     uchar        buf[50];        /* Scratch buffer                            */
  265.     uchar        expunge = 0;    /* If true, we have been expunged            */
  266.     uchar        done;            /* Used for loop termination                */
  267.  
  268.     SysBase = (struct ExecBase *)AbsExecBase;    /* Get pointer to exec        */
  269.     DOSBase = OpenLibrary("dos.library", 0);    /* and AmigaDOS                */
  270.     myproc    = (Proc *)FindTask(0L);
  271.  
  272.     /*
  273.      *    The debugging uses the serial port, at current settings. Connect
  274.      *    a dumb terminal (or a modem with echo mode enabled + a comms package)
  275.      *    to see the output.
  276.      */
  277.     DB(P("Started debugging for custom device\n"));
  278.  
  279.     /*
  280.      *        Initial startup message. This is where we get our device
  281.      *        parameters from (device name, etc.)
  282.      */
  283.     {
  284.         register DosPacket *mypkt;
  285.  
  286.         mypkt  = taskwait(myproc);
  287.         ioport = CreatePort(0, 0);
  288.  
  289.         if (ioport) {
  290.             struct FileSysStartupMsg *fssm;
  291.             uchar *dname;
  292.  
  293.             mynode    = BADDR(mypkt->dp_Arg3);
  294.             fssm    = BADDR(mynode->dn_Startup);
  295.             dname   = BADDR(mypkt->dp_Arg1);
  296.             if (dname) {
  297.                 strncpy(devname, dname+1, *dname);
  298.                 devname[*dname] = 0;
  299.             } else
  300.                 devname[0] = 0;
  301.             
  302.             if (!fssm) {
  303.                 returnpkt(mypkt, myproc, DOSFALSE, ERROR_DEVICE_NOT_MOUNTED);
  304.                 branchto exit;
  305.             }
  306.                 
  307.             default_device    = (uchar *)BADDR(fssm->fssm_Device) + 1;
  308.             default_unit      = fssm->fssm_Unit;
  309.             default_flags     = fssm->fssm_Flags;
  310.             env               = BADDR(fssm->fssm_Environ);
  311.             blocksize          = env->de_SizeBlock * 4;
  312.             block_offset_mask = blocksize - 1;
  313.             block_align_mask  = ~block_offset_mask;
  314.             numcyls              = env->de_HighCyl - env->de_LowCyl + 1;
  315.             numblocks          = env->de_BlocksPerTrack *
  316.                                 env->de_Surfaces * numcyls;
  317.             cylsize           = blocksize * env->de_BlocksPerTrack *
  318.                                 env->de_Surfaces;
  319.             startblock          = cylsize * env->de_LowCyl;
  320.  
  321.             maskdata->xferbuffer = NULL;
  322.             maskdata->xfermask   = env->de_Mask;
  323.  
  324.             /*
  325.              *        Now try and initialise an i/o port for ourselves.
  326.              *        If we can't do it, have to fail
  327.              */
  328.             ioreq = CreateStdIO(ioport);
  329.             if (!ioreq || OpenDevice(default_device, default_unit,
  330.                                      ioreq, default_flags) != 0
  331.                        || (oneblock = AllocMem(blocksize, 0)) == NULL) {
  332.                 returnpkt(mypkt, myproc, DOSFALSE, ERROR_NO_FREE_STORE);
  333.                 branchto exit;
  334.             }
  335.             mynode->dn_Task = &myproc->pr_MsgPort;
  336.             returnpkt(mypkt, myproc, DOSTRUE, 0);
  337.         } else {
  338.             returnpkt(mypkt, myproc, DOSFALSE, ERROR_NO_FREE_STORE);
  339.             branchto exit;
  340.         }
  341.         DB(P("'%s', %ld flags %ld\n",
  342.                 default_device, default_unit, default_flags));
  343.     }
  344.  
  345. top:
  346.  
  347.     /*
  348.      *        Main loop. We sit here waiting for DOS requests and handle
  349.      *        them as best we can.
  350.      */
  351.     done = 0;
  352.     while (!done) {
  353.         register DosPacket *mypkt;        /* Dos packet received        */
  354.         register MyFile *file;            /* Pointer to current File    */
  355.         long type;                        /* Type of packet            */
  356.  
  357.         mypkt = taskwait(myproc);        /* Wait/get next packet     */
  358.         type  = mypkt->dp_Type;            /* Packet type                */
  359.         mypkt->dp_Res1 = DOSTRUE;        /* Default return value     */
  360.         mypkt->dp_Res2 = 0;             /* Default no error         */
  361.  
  362.         /*
  363.          *        Extract File pointer.  Doesn't apply to Open()
  364.          */
  365.         file = (MyFile *)mypkt->dp_Arg1;
  366.  
  367.         DB(P("%-12s arg3 %8lx, file: %8lx arg2: %8lx\n",
  368.               actname(mypkt->dp_Type), mypkt->dp_Arg3, file, mypkt->dp_Arg2));
  369.  
  370.         switch (type) {
  371.  
  372.         case ACTION_FIND_INPUT:
  373.         case ACTION_FIND_OUTPUT:
  374.         case ACTION_FIND_UPDATE:
  375.             /*
  376.              *        Handles opening any file on this device.
  377.              */
  378.             {
  379.                 IBMInfo ibm[1];    
  380.                 uchar *name = BADDR(mypkt->dp_Arg3);
  381.  
  382.                 /* Parse the given filename */
  383.                 if (*name)
  384.                     strncpy(buf, name + 1, *name);
  385.                 buf[*name] = '\0';
  386.                 DB(P("Open file: %s\n", buf));
  387.  
  388.                 if (!ParseFilename(buf, ibm, env) ||
  389.                     (ibm->newfileonly && type != ACTION_FIND_OUTPUT)) {
  390.                     mypkt->dp_Res1 = DOSFALSE;
  391.                     mypkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
  392.                 } else {
  393.                     file = OpenMyFile(ibm, blocksize);
  394.                     if (file) {
  395.                         struct FileHandle *fh = BADDR(mypkt->dp_Arg1);
  396.  
  397.                         memset(fh, 0, sizeof(*fh));
  398.                         fh->fh_Arg1 = (long)file;
  399.                         fh->fh_Pos  = -1;
  400.                         fh->fh_End  = -1;
  401.                         fh->fh_Type = &myproc->pr_MsgPort;
  402.                         opencount++;
  403.                     } else {
  404.                         mypkt->dp_Res1 = DOSFALSE;
  405.                         mypkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
  406.                     }
  407.                 }
  408.             }
  409. returnpkt:
  410.             returnpktplain(mypkt, myproc);
  411.             break;
  412.  
  413.         case ACTION_END:                            /* CLOSE    */
  414.             DB(P("Close File %08lx\n", file));
  415.             FreeMem(file, sizeof(*file));
  416.             returnpktplain(mypkt, myproc);
  417.             --opencount;
  418.             break;
  419.  
  420.         case ACTION_READ:                    /* Arg2: buffer */
  421.             {                                /* Arg3: size    */
  422.                 uchar *buffer   = (uchar *)mypkt->dp_Arg2;
  423.                 ULONG oldoffset = file->offset;
  424.                 ULONG size = mypkt->dp_Arg3;
  425.                 ULONG done = 0;
  426.                 int error  = 0;
  427.  
  428.                 DB(P("Read File %08lx ", file));
  429.                 DB(P("buffer %08lx  size %08lx offset %08lx\n",
  430.                                 buffer, size, file->offset));
  431. #if SNOOP
  432.                 P("Read : Block No. = %8lx, # Blocks = %8lx\n",
  433.                    file->offset >> 9, size >> 9);
  434. #endif
  435.                 /*
  436.                  *     Don't read past the end of the virtual file
  437.                  */
  438.                 done = file->maxoffset - file->offset;
  439.                 if (size > done)
  440.                      size = done;
  441.                 mypkt->dp_Res1 = size;
  442.  
  443.                 /*
  444.                  *        Handle reads from the bootblock if this is
  445.                  *        an autoboot disk. Will also be used if any other
  446.                  *        kind of header is attached to the front of the
  447.                  *        file (such as a mountlist entry, for example.)
  448.                  */
  449.                 if (size && file->offset < file->datasize) {
  450.                     ULONG bsize = MIN(size, file->datasize - file->offset);
  451.                     uchar *header = (uchar *)file->data;
  452.                         
  453.                     memcpy(buffer, header + file->offset, bsize);
  454.                     file->offset += bsize;
  455.                     size   -= bsize;
  456.                     buffer += bsize;
  457.                 }
  458.                 if (size && (file->offset & block_offset_mask)) {
  459.                     /*
  460.                      *        We want all disk i/o via scsi.device to be
  461.                      *        on sector boundaries, to prevent scsi.device
  462.                      *        getting confused. So, if we are not aligned
  463.                      *        on a sector, we read the whole sector anyway,
  464.                      *        then just copy what we need.
  465.                      */
  466.                     ULONG blkoffset  = file->offset & block_align_mask;
  467.                     ULONG byteoffset = file->offset & block_offset_mask;
  468.                     ULONG numbytes   = MIN(blocksize - byteoffset, size);
  469.  
  470.                     DB(P("Read: first sectors, offset = %8ld, num = %8ld\n",
  471.                           file->offset, numbytes));
  472.                     error |= PerformIO(maskdata, ioreq, CMD_READ, oneblock,
  473.                                        file->start + blkoffset, blocksize);
  474.                     memcpy(buffer, oneblock + file->offset-blkoffset, numbytes);
  475.                     file->offset += numbytes;
  476.                     buffer       += numbytes;
  477.                     size         -= numbytes;
  478.                 }
  479.                 /*
  480.                  *        Now we're sector-aligned. Read in as many full
  481.                  *        blocks as possible. (This is by far the most
  482.                  *        common case, since almost all of our requests will
  483.                  *        come from the bridgeboard software, which always
  484.                  *        looks for data on sector boundaries.
  485.                  */        
  486.                 if (size >= blocksize) {
  487.                     ULONG numbytes = size & block_align_mask;
  488.  
  489.                     DB(P("Read: full sectors, offset = %8ld, num = %8ld\n",
  490.                           file->offset, numbytes));
  491.                     error |= PerformIO(maskdata, ioreq, CMD_READ, buffer,
  492.                                          file->start + file->offset, numbytes);
  493.                     file->offset += numbytes;
  494.                     buffer       += numbytes;
  495.                     size         -= numbytes;
  496.                 }
  497.                 /*
  498.                  *        Now check for any partial read from the final
  499.                  *        sector. As above, we handle this by reading the
  500.                  *        entire sector into a local buffer, then copying
  501.                  *        the required data into the caller's buffer.
  502.                  */
  503.                 if (size) {
  504.                     DB(P("Read: last sector,   offset = %8ld, num = %8ld\n",
  505.                           file->offset, size));
  506.                     error |= PerformIO(maskdata, ioreq, CMD_READ, oneblock,
  507.                                          file->start + file->offset, blocksize);
  508.                     memcpy(buffer, oneblock, size);
  509.                     file->offset += size;
  510.                     buffer       += size;
  511.                     size          = 0;
  512.                 }
  513.                 /*
  514.                  *        Now check did an error occur at any stage
  515.                  */
  516.                 if (error) {
  517.                     mypkt->dp_Res1 = -1;
  518.                     mypkt->dp_Res2 = ERROR_READ_PROTECTED; /* Seems best */
  519.                     file->offset   = oldoffset;
  520.                 }
  521.             }
  522.             branchto returnpkt;
  523.  
  524.         case ACTION_WRITE:                    /* Arg2: buffer */
  525.             {                                /* Arg3: size    */
  526.                 uchar *buffer   = (uchar *)mypkt->dp_Arg2;
  527.                 ULONG oldoffset = file->offset;
  528.                 ULONG size = mypkt->dp_Arg3;
  529.                 ULONG done = 0;
  530.                 int error  = 0;
  531.  
  532.                 DB(P("Write: File = %8lx ", file));
  533.                 DB(P("buffer %08lx  size %08lx offset %08lx\n",
  534.                                 buffer, size, file->offset));
  535. #if SNOOP
  536.                 P("Write: Block No. = %8lx, # Blocks = %8lx\n",
  537.                    file->offset >> 9, size >> 9);
  538. #endif
  539.                 /*
  540.                  *     Don't write past the end of the virtual file
  541.                  */
  542.                 done = file->maxoffset - file->offset;
  543.                 if (size > done)
  544.                      size = done;
  545.                 mypkt->dp_Res1 = size;
  546.  
  547.                 /*
  548.                  *        Handle writes to the header of the file, if any.
  549.                  *        We do this by just plain ignoring such writes,
  550.                  *        since they aren't stored permanently anyway.
  551.                  */
  552.                 if (size && file->offset < file->datasize) {
  553.                     ULONG bsize = MIN(size, file->datasize - file->offset);
  554.                         
  555.                     file->offset += bsize;
  556.                     size   -= bsize;
  557.                     buffer += bsize;
  558.                 }
  559.                 if (size && (file->offset & block_offset_mask)) {
  560.                     /*
  561.                      *        We want all disk i/o via scsi.device to be
  562.                      *        on sector boundaries, to prevent scsi.device
  563.                      *        getting confused. So, if we are not aligned
  564.                      *        on a sector, we read the whole sector, modify
  565.                      *        what's necessary, then write it again.
  566.                      */
  567.                     ULONG blkoffset  = file->offset & block_align_mask;
  568.                     ULONG byteoffset = file->offset & block_offset_mask;
  569.                     ULONG numbytes   = MIN(blocksize - byteoffset, size);
  570.  
  571.                     DB(P("Write: first sectors, offset = %8ld, num = %8ld\n",
  572.                           file->offset, numbytes));
  573.                     error |= PerformIO(maskdata, ioreq, CMD_READ, oneblock,
  574.                                        file->start + blkoffset, blocksize);
  575.                     memcpy(oneblock + file->offset-blkoffset, buffer, numbytes);
  576.                     if (!error) {
  577.                         error |= PerformIO(maskdata, ioreq, CMD_WRITE,
  578.                                  oneblock, file->start + blkoffset, blocksize);
  579.                     }
  580.                     file->offset += numbytes;
  581.                     buffer       += numbytes;
  582.                     size         -= numbytes;
  583.                 }
  584.                 /*
  585.                  *        Now we're sector-aligned. Write as many full
  586.                  *        blocks as possible. (This is by far the most
  587.                  *        common case, since almost all of our requests will
  588.                  *        come from the bridgeboard software, which always
  589.                  *        mainpulates data on sector boundaries.
  590.                  */        
  591.                 if (size >= blocksize) {
  592.                     ULONG numbytes = size & block_align_mask;
  593.  
  594.                     DB(P("Write: full sectors, offset = %8ld, num = %8ld\n",
  595.                           file->offset, numbytes));
  596.                     error |= PerformIO(maskdata, ioreq, CMD_WRITE, buffer,
  597.                                          file->start + file->offset, numbytes);
  598.                     file->offset += numbytes;
  599.                     buffer       += numbytes;
  600.                     size         -= numbytes;
  601.                 }
  602.                 /*
  603.                  *        Now check for any partial writes to the final
  604.                  *        sector. As above, we handle this by reading the
  605.                  *        entire sector into a local buffer, modifying the
  606.                  *        desired info, then writing it back again.
  607.                  */
  608.                 if (size) {
  609.                     DB(P("Write: last sector,   offset = %8ld, num = %8ld\n",
  610.                           file->offset, size));
  611.                     error |= PerformIO(maskdata, ioreq, CMD_READ, oneblock,
  612.                                          file->start + file->offset, blocksize);
  613.                     memcpy(oneblock, buffer, size);
  614.                     if (!error) {
  615.                         error |= PerformIO(maskdata, ioreq, CMD_WRITE, oneblock,
  616.                                  file->start + file->offset, blocksize);
  617.                     }
  618.                     file->offset += size;
  619.                     buffer       += size;
  620.                     size          = 0;
  621.                 }
  622.                 /*
  623.                  *        Now check did an error occur at any stage
  624.                  */
  625.                 if (error) {
  626.                     mypkt->dp_Res1 = -1;
  627.                     mypkt->dp_Res2 = ERROR_WRITE_PROTECTED; /* Seems best */
  628.                     file->offset   = oldoffset;
  629.                 }
  630.             }
  631.             branchto returnpkt;
  632.  
  633.         case ACTION_DIE:
  634.             expunge = 1;
  635.             if (opencount == 0)
  636.                  done = 1;
  637.             branchto returnpkt;
  638.  
  639.         case ACTION_SEEK:
  640.         {
  641.             long offset = -1;
  642.  
  643.             switch (mypkt->dp_Arg3) {    /* seek mode */
  644.  
  645.             case OFFSET_BEGINNING:
  646.                 offset = mypkt->dp_Arg2;
  647.                 break;
  648.  
  649.             case OFFSET_CURRENT:
  650.                 offset = file->offset + mypkt->dp_Arg2;
  651.                 break;
  652.  
  653.             case OFFSET_END:
  654.                 offset = file->maxoffset + mypkt->dp_Arg2;
  655.                 break;
  656.             }
  657.             if (offset >= 0 && offset <= file->maxoffset) {
  658.                 mypkt->dp_Res1 = offset;
  659.                 file->offset   = offset;
  660.             } else {
  661.                 mypkt->dp_Res1 = -1;
  662.                 mypkt->dp_Res2 = ERROR_SEEK_ERROR;
  663.             }
  664.             branchto returnpkt;
  665.         }
  666.  
  667.         case ACTION_LOCATE_OBJECT:    /* Sort of like Open() */
  668.         {
  669.             uchar   *name = BADDR(mypkt->dp_Arg2);
  670.             MyLock  *mylock = NULL;
  671.             IBMInfo ibm[1];    
  672.  
  673.             /* Parse the given filename */
  674.             if (*name)
  675.                 strncpy(buf, name + 1, *name);
  676.             buf[*name] = '\0';
  677.             DB(P("Lock file: %s\n", buf));
  678.  
  679.             if (!ParseFilename(buf, ibm, env) ||
  680.                 (ibm->newfileonly && mypkt->dp_Arg3 != ACCESS_WRITE)) {
  681.                 mypkt->dp_Res1 = DOSFALSE;
  682.                 mypkt->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
  683.             } else if (mylock = AllocMem(sizeof (MyLock), MEMF_CLEAR)) {
  684.                 /*
  685.                     *        Allocated a lock node okay, so now just copy
  686.                     *        the information about the partition gleaned
  687.                     *        from the filename. This can then be examined
  688.                     *        by Examine() etc. if necessary.
  689.                     */
  690.                 mylock->fl.fl_Volume = CTOB(mynode);
  691.                 mylock->fl.fl_Access = mypkt->dp_Arg3;
  692.                 mylock->fl.fl_Task   = &myproc->pr_MsgPort;
  693.                 mylock->ibm          = *ibm;
  694.                 opencount++;
  695.             } else {
  696.                 mypkt->dp_Res2 = ERROR_NO_FREE_STORE;
  697.             }
  698.             mypkt->dp_Res1 = CTOB(mylock);
  699.             branchto returnpkt;
  700.         }
  701.  
  702.         case ACTION_EXAMINE_OBJECT:
  703.         {
  704.             struct FileInfoBlock *fib = BADDR(mypkt->dp_Arg2);
  705.             MyLock *mylock = BADDR(mypkt->dp_Arg1);
  706.             IBMInfo *ibm = &mylock->ibm;
  707.  
  708.             fib->fib_DiskKey        = ibm->startblock;
  709.             fib->fib_DirEntryType    = ST_FILE;
  710.             strcpy(fib->fib_FileName+1, "JanusFile");
  711.             fib->fib_FileName[0]     = strlen(fib->fib_FileName+1);
  712.             fib->fib_Protection      = 0;
  713.             fib->fib_EntryType       = fib->fib_DirEntryType;
  714.             fib->fib_Size            = ibm->numblocks * blocksize;
  715.             fib->fib_NumBlocks       = ibm->numblocks;
  716.             DateStamp(&fib->fib_Date);    /* Use current time */
  717.             strcpy(fib->fib_Comment+1, DEF_COMMENT);
  718.             fib->fib_Comment[0]     = strlen(fib->fib_Comment+1);
  719.             branchto returnpkt;
  720.         }
  721.  
  722.         case ACTION_EXAMINE_NEXT:
  723.             mypkt->dp_Res1 = DOSFALSE;
  724.             mypkt->dp_Res2 = ERROR_NO_MORE_ENTRIES;
  725.             branchto returnpkt;
  726.  
  727.         case ACTION_COPY_DIR:
  728.         {
  729.             MyLock *mylock = NULL;
  730.  
  731.             if (mylock = AllocMem(sizeof (MyLock), MEMF_CLEAR)) {
  732.                 *mylock = *(MyLock *)BADDR(mypkt->dp_Arg1);
  733.                 opencount++;
  734.             } else {
  735.                 mypkt->dp_Res2 = ERROR_NO_FREE_STORE;
  736.             }
  737.             mypkt->dp_Res1 = CTOB(mylock);
  738.             branchto returnpkt;
  739.         }
  740.  
  741.         case ACTION_FREE_LOCK:
  742.         {
  743.             if (mypkt->dp_Arg1) {
  744.                 FreeMem(BADDR(mypkt->dp_Arg1), sizeof(MyLock));
  745.                 opencount--;
  746.             }
  747.             branchto returnpkt;
  748.         }
  749.  
  750.         case ACTION_FH_FROM_LOCK:
  751.         {
  752.             /*
  753.              *        The lock we're passed in has already been initialised
  754.              *        with the appropriate disk info, so we just pretty
  755.              *        much duplicate the Open() code here to create and
  756.              *        initialise the file handle based on this.
  757.              *        opening. This 'hack' only works because we are
  758.              *        not interpreting the filename in any particular way.
  759.              */
  760.             MyLock *mylock = BADDR(mypkt->dp_Arg2);
  761.  
  762.             file = OpenMyFile(&mylock->ibm, blocksize);
  763.             if (file) {
  764.                 struct FileHandle *fh = BADDR(mypkt->dp_Arg1);
  765.  
  766.                 memset(fh, 0, sizeof(*fh));
  767.                 fh->fh_Arg1 = (long)file;
  768.                 fh->fh_Pos  = -1;
  769.                 fh->fh_End  = -1;
  770.                 fh->fh_Type = &myproc->pr_MsgPort;
  771.                 FreeMem(mylock, sizeof (MyLock));
  772.             } else {
  773.                 mypkt->dp_Res1 = DOSFALSE;
  774.                 mypkt->dp_Res2 = ERROR_NO_FREE_STORE;
  775.             }
  776.             branchto returnpkt;
  777.         }
  778.  
  779.         case ACTION_PARENT:
  780.             mypkt->dp_Res1 = 0;
  781.             mypkt->dp_Res2 = ERROR_DIR_NOT_FOUND;
  782.             branchto returnpkt;
  783.  
  784.         case ACTION_DISK_INFO:
  785.         case ACTION_INFO:
  786.         {
  787.             struct InfoData *info;
  788.             
  789.             if (type == ACTION_DISK_INFO)
  790.                 info = BADDR(mypkt->dp_Arg1);
  791.             else /* ACTION_INFO */
  792.                 info = BADDR(mypkt->dp_Arg2);
  793.  
  794.             info->id_NumSoftErrors = 0;
  795.             info->id_UnitNumber    = default_unit;
  796.             info->id_DiskState     = ID_VALIDATED;
  797.             info->id_NumBlocks     = numblocks;
  798.             info->id_NumBlocksUsed = numblocks;
  799.             info->id_BytesPerBlock = env->de_SizeBlock * 4;
  800.             info->id_DiskType      = ID_DOS_DISK;
  801.             info->id_VolumeNode    = CTOB(mynode);
  802.             info->id_InUse         = 1;
  803.             branchto returnpkt;
  804.         }
  805.             
  806.         case ACTION_CURRENT_VOLUME:
  807.             mypkt->dp_Res1 = CTOB(mynode);
  808.             branchto returnpkt;
  809.  
  810.         case ACTION_IS_FILESYSTEM:
  811.         case ACTION_SET_PROTECT:
  812.         case ACTION_SET_COMMENT:
  813.         case ACTION_SET_DATE:
  814.         case ACTION_INHIBIT:
  815.         case ACTION_FLUSH:
  816.             /*
  817.              *        We make these into NOP's rather than letting them fail,
  818.              *        to stop application programs from getting worried.
  819.              */
  820.             branchto returnpkt;
  821.  
  822.         default:
  823.             returnpkt(mypkt, myproc, DOSFALSE, ERROR_ACTION_NOT_KNOWN);
  824.             break;
  825.         } /* end switch */
  826.  
  827.         DB(P("             Res1 %08lx   Res2 %08lx\n",
  828.               mypkt->dp_Res1, mypkt->dp_Res2));
  829.     } /* end while (done) */
  830.  
  831.     /*
  832.      *    Can only exit if no messages pending.  There might be a window
  833.      *    here, but there is nothing that can be done about it.
  834.      */
  835.  
  836. #if DEBUG
  837.     if (!expunge)
  838.         branchto top;
  839. #endif
  840.  
  841.     Forbid();
  842.     if (taskpktrdy(myproc)) {
  843.         Permit();
  844.         branchto top;
  845.     }
  846.  
  847.     mynode->dn_Task = NULL;
  848.     Permit();
  849.  
  850. exit:
  851.     if (ioreq->io_Device)    CloseDevice(ioreq);
  852.     if (ioreq)                DeleteStdIO(ioreq);
  853.     if (ioport)                DeletePort(ioport);
  854.     if (oneblock)            FreeMem(oneblock, blocksize);
  855.  
  856.     if (maskdata->xferbuffer) {
  857.         FreeMem(maskdata->xferbuffer, maskdata->xfersize);
  858.         maskdata->xferbuffer = NULL;
  859.     }
  860.  
  861.     DB(P("Exiting from device.\n"));
  862.  
  863.     /* We are a process "so we fall off the end of the world" */
  864.  
  865.     if (expunge) {
  866.         BPTR mycode = mynode->dn_SegList;
  867.  
  868.         Forbid();
  869.         mynode->dn_SegList = NULL;
  870.         UnLoadSeg(mycode);
  871.         /* We execute in unallocated memory now... and Forbid()den. */
  872.     }
  873.     CloseLibrary(DOSBase);
  874.  
  875.     /* MUST fall through */
  876. }
  877.  
  878. /*
  879.  *        PerformIO(maskdata, ioreq, cmd, buffer, offset, size)
  880.  *
  881.  *        Carries out specified IO as per parameters and returns when
  882.  *        complete. Maskdata is used to check for disk i/o which doesn't
  883.  *        fall within the scope of the supplied buffer. Such i/o is
  884.  *        handled by passing it through an intermediary buffer first,
  885.  *        then copying it by hand to the required destination.
  886.  *
  887.  *        Note that offset and size must be multiples of the device's
  888.  *        blocksize, or the device (especially scsi.device) may lock up.
  889.  *
  890.  *        Returns -1 if an error occurred.
  891.  */
  892. ULONG PerformIO(MaskData *maskdata, IOStdReq *ioreq, ULONG cmd, void *buffer,
  893.                 ULONG offset, ULONG size)
  894. {
  895.     DB(P("PerfIO buffer %08lx  size %08lx offset %08lx\n",
  896.                   buffer, size, offset));
  897.     ioreq->io_Command    = cmd;
  898.     ioreq->io_Length    = size;
  899.     ioreq->io_Data        = buffer;
  900.     ioreq->io_Offset    = offset;
  901.     DoIO(ioreq);
  902.     if (ioreq->io_Actual != size || ioreq->io_Error)
  903.         return (-1);
  904.     else
  905.         return (0);
  906. }
  907.  
  908. /*
  909.  *        ParseFilename(filename, ibminfo, diskenv)
  910.  *
  911.  *        This function parses the specified filename and fills in the
  912.  *        ibminfo structure with details of the geometry of the disk as
  913.  *        it should appear to the PC. Defaults are taken from diskenv as
  914.  *        necessary.
  915.  *
  916.  *        We can fairly easily calculate the IBM sector and head values.
  917.  *        We manipulate the starting block info a bit, however, to
  918.  *        arrange that the IBM partition data starts an integral number
  919.  *        of cylinders from the start of the physical disk. This is not
  920.  *        strictly required (after all, the bridgeboard software is
  921.  *        accessing the whole thing as a file anyway and couldn't care
  922.  *        less) but it helps filesystems like the MSDOSFileSystem in
  923.  *        CrossDOS which expect such things. It costs us a tiny amount
  924.  *        of usable disk space, but not enough to worry about.
  925.  *        
  926.  *        The filename itself can have a number of options, as detailed
  927.  *        at the start of this source code. If the filename doesn't
  928.  *        make sense, a zero is returned, else non-zero.
  929.  */
  930. int ParseFilename(uchar *filename, IBMInfo *ibm, DosEnvec *env)
  931. {
  932.     char *p;
  933.     long startblock;
  934.     long numblocks;
  935.     long icylsize;
  936.  
  937.     ibm->heads       = 0;
  938.     ibm->sectors     = 0;
  939.     ibm->isautoboot  = 0;
  940.     ibm->newfileonly = 0;
  941.  
  942.     p = filename;
  943.     while (*p) {
  944.         if (*p == '/') {
  945.             p++;
  946.             if (strnicmp(p, "AMIGA", 5) == 0) {
  947.                 ibm->heads   = env->de_Surfaces;
  948.                 ibm->sectors = env->de_BlocksPerTrack;
  949.                 p += 5;
  950.             } else if (strnicmp(p, "IBM", 3) == 0) {
  951.                 ibm->heads   = IBM_HEADS;
  952.                 ibm->sectors = IBM_SECTORS;
  953.                 p += 3;
  954.             } else if (strnicmp(p, "AUTO", 4) == 0) {
  955.                 ibm->isautoboot = 1;
  956.                 p += 4;
  957.             } else if (strnicmp(p, "NEW", 3) == 0) {
  958.                 ibm->newfileonly = 1;
  959.                 p += 3;
  960.             } else if (*p == 's' || *p == 'S') {
  961.                 ibm->sectors = atoi(++p);
  962.                 while (*p && *p != '/')
  963.                     p++;
  964.             } else if (*p == 'h' || *p == 'H') {
  965.                 ibm->heads = atoi(++p);
  966.                 while (*p && *p != '/')
  967.                     p++;
  968.             } else
  969.                 return (0);
  970.         } else
  971.             *p++;
  972.     }
  973.     if (ibm->heads   == 0)    ibm->heads   = IBM_HEADS;
  974.     if (ibm->sectors == 0)    ibm->sectors = IBM_SECTORS;
  975.  
  976.     icylsize   = ibm->heads * ibm->sectors;
  977.     startblock = env->de_LowCyl * env->de_Surfaces * env->de_BlocksPerTrack;
  978.     numblocks  = env->de_BlocksPerTrack * env->de_Surfaces *
  979.                  (env->de_HighCyl - env->de_LowCyl + 1);
  980.  
  981.     ibm->startblock = ((startblock + icylsize - 1) / icylsize) * icylsize;
  982.     ibm->numcyls    = (numblocks - (ibm->startblock - startblock)) / icylsize;
  983.     ibm->numblocks  = ibm->numcyls * icylsize;
  984.     return (1);
  985. }
  986.  
  987. /*
  988.  *        OpenMyFile(IBMInfo *ibm, blocksize)
  989.  *
  990.  *        Attempts to create and initialise a MyFile structure suitable
  991.  *        for accessing the section of the disk defined in ibm. This
  992.  *        includes creating a fake Janus bootblock, just in case it's
  993.  *        needed. Returns a pointer to the file structure if successfull,
  994.  *        or NULL if not enough memory.
  995.  *
  996.  *        Blocksize is the physical size of a block on the disk.
  997.  */
  998. MyFile *OpenMyFile(IBMInfo *ibm, ULONG blocksize)
  999. {
  1000.     MyFile *file;
  1001.     BootBlock *bb;
  1002.     ULONG headersize = 0;
  1003.  
  1004.     if (ibm->isautoboot)
  1005.         headersize = sizeof(BootBlock);
  1006.  
  1007.     file = (MyFile *)AllocMem(sizeof(MyFile) + headersize, MEMF_CLEAR);
  1008.     if (!file)
  1009.         return (NULL);
  1010.  
  1011.     file->start         = ibm->startblock * blocksize;
  1012.     file->offset     = 0;
  1013.     file->maxoffset     = ibm->numblocks * blocksize;
  1014.     file->datasize   = headersize;
  1015.  
  1016.     if (ibm->isautoboot) {
  1017.         file->maxoffset += headersize;
  1018.         file->start     -= headersize;
  1019.         bb = (BootBlock *)file->data;
  1020.         strcpy(bb->title, "ABOOT");
  1021.         bb->heads     = ibm->heads;
  1022.         bb->sectors   = ibm->sectors;
  1023.         bb->cylinders = ibm->numcyls;
  1024.     }
  1025.     return (file);
  1026. }
  1027.  
  1028. #if DEBUG
  1029. /*
  1030.  *        Translates action numbers to action names to ease debugging
  1031.  */
  1032. uchar *actname(int actnum)
  1033. {
  1034.     switch (actnum) {
  1035.         case ACTION_FIND_INPUT:        return ("FIND_INPUT");
  1036.         case ACTION_FIND_OUTPUT:    return ("FIND_OUTPUT");
  1037.          case ACTION_FIND_UPDATE:    return ("FIND_UPDATE");
  1038.         case ACTION_END:            return ("END");
  1039.          case ACTION_READ:            return ("READ");
  1040.          case ACTION_WRITE:            return ("WRITE");
  1041.         case ACTION_DIE:            return ("DIE");
  1042.         case ACTION_SEEK:            return ("SEEK");
  1043.          case ACTION_LOCATE_OBJECT:    return ("LOCATE_OBJ");
  1044.          case ACTION_EXAMINE_OBJECT:    return ("EXAMINE_OBJ");
  1045.         case ACTION_EXAMINE_NEXT:    return ("EXAMINE_NEXT");
  1046.         case ACTION_COPY_DIR:        return ("COPY_DIR");
  1047.         case ACTION_FREE_LOCK:        return ("FREE_LOCK");
  1048.         case ACTION_FH_FROM_LOCK:    return ("FH_FROM_LOCK");
  1049.         case ACTION_PARENT:            return ("PARENT");
  1050.         case ACTION_DISK_INFO:        return ("DISK_INFO");
  1051.         case ACTION_INFO:            return ("INFO");
  1052.         case ACTION_IS_FILESYSTEM:    return ("IS_FILESYS");
  1053.         case ACTION_SET_PROTECT:    return ("SET_PROTECT");
  1054.         case ACTION_SET_COMMENT:    return ("SET_COMMENT");
  1055.         case ACTION_SET_DATE:        return ("SET_DATE");
  1056.         case ACTION_INHIBIT:        return ("INHIBIT");
  1057.         case ACTION_CURRENT_VOLUME:    return ("CURRENT_VOL");
  1058.         case ACTION_FLUSH:            return ("FLUSH");
  1059.         default:
  1060.             DB(P("Action_%04ld", actnum));
  1061.             return (" ");
  1062.     }
  1063. }
  1064. #endif
  1065.